<template>
  <ElForm
    class="form-container"
    ref="formRef"
    v-bind="$attrs"
    :model="modelValue"
    :rules="rules"
    :validate-on-rule-change="false"
    :style="{ '--columns': props.columns, '--gap': gap }"
    @submit.prevent="handleSubmit"
  >
    <ElFormItem
      v-for="(item, index) in formItems"
      :key="item.formItem.prop"
      v-bind="item.formItem"
      v-show="isExpand ? true : index < props.columns * props.collapseRows - 1"
      :class="item.class"
      :data-show="
        item.formItem.prop in fieldDep
          ? fieldDep[item.formItem.prop].show
          : true
      "
    >
      <template #label>
        <slot :name="`${item.formItem.prop}Label`" :formItem="item">
          <div class="form-item_label">
            <span>{{ item.formItem.label }}</span>
            <!-- help 提示 -->
            <ElTooltip v-if="item.help" placement="top" :content="item.help">
              <ElIcon>
                <Warning />
              </ElIcon>
            </ElTooltip>
            {{ item.formItem.label && props.colon ? " :" : "" }}
          </div>
        </slot>
      </template>
      <template #default>
        <!-- 前缀插槽 -->
        <slot :name="`${item.formItem.prop}Prefix`" :formItem="item">
          <component v-if="item.prefix" :is="item.prefix" />
        </slot>
        <slot :name="item.formItem.prop" :formItem="item">
          <!-- 存在 render -->
          <component
            v-if="item.render"
            :is="item.render"
            v-bind="item.componentProps"
          ></component>
          <!-- TODO: 暂时分开写 考虑合并 -->
          <component
            v-else
            :is="getComponent(item)"
            v-model:[getComponentModelValue(item)]="
              modelValue[item.formItem.prop]
            "
            :disabled="fieldDep[item.formItem.prop]?.disabled ?? false"
            v-bind="getComponentProps(item)"
          >
          </component>
        </slot>
        <!-- 后缀插槽 -->
        <slot :name="`${item.formItem.prop}Suffix`" :formItem="item">
          <component v-if="item.suffix" :is="item.suffix" />
        </slot>
      </template>
    </ElFormItem>
    <ElFormItem v-if="isShowLastFormItem">
      <!-- 自定义操作按钮 -->
      <slot name="actions">
        <ElButton @click="handleReset" v-show="!!props.resetText">
          {{ props.resetText }}
        </ElButton>
        <ElButton
          type="primary"
          :native-type="props.enterSubmit ? 'submit' : 'button'"
          @click="props.enterSubmit ? () => {} : handleSubmit()"
          v-show="!!props.submitText"
        >
          {{ props.submitText }}
        </ElButton>
      </slot>
      <ElButton
        link
        type="primary"
        class="expand"
        v-show="
          props.columns * props.collapseRows < formItems.length && isCollapse
        "
        @click="handleExpand"
      >
        {{ isExpand ? t("common.Collapse") : t("common.Expand") }}
        <ElIcon class="el-icon--right" :class="!isExpand ? 'collapse' : ''">
          <ArrowUp />
        </ElIcon>
      </ElButton>
    </ElFormItem>
  </ElForm>
</template>

<script setup>
import {
  useTemplateRef,
  ref,
  watch,
  readonly,
  computed,
  useSlots,
  useAttrs,
} from "vue";
import {
  buildFormProps,
  formEmits,
  inputType,
  componentDefaultValue,
  normalizeProps,
  getComponentProps,
  getComponent,
  getComponentModelValue,
} from "./index";
import { isArray } from "@/utils";
import { useI18n } from "vue-i18n";
import { service } from "@/utils/request";
defineOptions({
  name: "Form",
});
const { t } = useI18n();
const props = defineProps(buildFormProps());
const emits = defineEmits(formEmits);
const modelValue = defineModel({ default: {} });
const slots = useSlots();
const formRef = useTemplateRef("formRef");
let resetFormData = {}; // 表单数据缓存(用于重置)
const remoteCache = ref({}); // 远程数据缓存
const isExpand = ref(true); // 是否展开
const rules = ref({}); //  表单验证规则
const formItems = ref([]); // 表单字段
let dependencies = {}; // 表单字段依赖信息
const fieldDep = ref({}); // 表单字段依赖执行结果
const attrs = useAttrs();
const watchStops = [];
// 列间距
const gap = computed(() => {
  const r = props.gap.toString();
  return /[A-z]$/.test(r) ? r : `${r}px`;
});
const isShowLastFormItem = computed(() => {
  return (
    props.formItems.length > 0 &&
    (!!(props.submitText && props.resetText) ||
      !!slots.actions ||
      !!props.isCollapse)
  );
});

watch(
  () => props.formItems,
  (newFormItems) => {
    const items = [];
    dependencies = {}; // 重置依赖信息
    resetFormData = {}; // 清空存储默认值
    fieldDep.value = {}; // 重置表单字段依赖执行结果
    for (const stop of watchStops) {
      stop();
    }
    watchStops.length = 0;
    for (const item of newFormItems) {
      const newItem = normalizeProps(item);
      const prop = newItem.formItem.prop;
      // item.rawProp = item.formItem.prop; // 保存原始 prop
      items.push(newItem);
      // 处理表单验证规则
      handleRule(newItem);
      // 处理表单值
      handleFormValue(newItem);
      // 初始收集依赖
      handleDependencies(newItem);
      // 初始派发更新
      dispatchFieldUpdate(prop, modelValue.value[prop]);
      // 判断有没有传递事件
      if (attrs.onFieldChange || attrs[`onFieldChange:${prop}`]) {
        const stop = watch(
          () => modelValue.value[prop],
          (value) => {
            if (value === undefined) {
              return;
            }
            fieldValueChange(prop, value, newItem);
          }
        );
        watchStops.push(stop);
      }
    }
    formItems.value = items;
  },
  { immediate: true, deep: true }
);

// 处理rules
function handleRule(item) {
  const prop = item.formItem.prop;
  const label = item.formItem.label;
  const trigger = inputType.includes(item.type) ? "blur" : "change";
  const isRequired = item.formItem.required;
  rules.value[prop] = [];
  if (isRequired) {
    const required = {
      required: true,
      message: inputType.includes(item.type)
        ? t("form.PleaseInput", { label })
        : t("form.PleaseSelect", { label }),
      trigger,
    };
    rules.value[prop].push(required);
  }
  // 如果有规则，则将规则添加到 rules 中
  if (item.rule) {
    const rule = isArray(item.rule) ? item.rule : [item.rule];
    rules.value[prop].push(...rule);
  }
  if (item.validator) {
    const validator = {
      validator: item.validator,
      trigger,
    };
    rules.value[prop].push(validator);
  }
}
// 处理默认值
function handleFormValue(item) {
  const prop = item.formItem.prop;
  if (!prop) {
    return;
  }
  const rawValue = modelValue.value[prop];
  const defaultValue =
    "defaultValue" in item
      ? item.defaultValue
      : componentDefaultValue[item.type];
  modelValue.value[prop] = rawValue ?? defaultValue;
  resetFormData[prop] = defaultValue;
}
// 处理依赖字段 收集依赖
function handleDependencies(item) {
  if (!item.dependencies) {
    return;
  }
  const prop = item.formItem.prop;
  const { triggerFields, show, disabled, rules, required, componentProps } =
    item.dependencies;
  const tf = triggerFields.filter((v) => v !== prop); // 排除依赖自身
  for (const p of tf) {
    const depFuncs = { show, disabled, rules, required, componentProps };
    if (p in dependencies) {
      dependencies[p].push([prop, depFuncs]);
    } else {
      dependencies[p] = [[prop, depFuncs]]; // 添加依赖 第二项为触发依赖函数
    }
  }
}
// 派发更新依赖字段
function dispatchFieldUpdate(prop, newValue) {
  // 检查是否存在依赖字段
  if (!(prop in dependencies)) {
    return;
  }
  for (const [p, depFuncs] of dependencies[prop]) {
    const obj = {};
    for (const fKey in depFuncs) {
      if (typeof depFuncs[fKey] !== "function") {
        continue;
      }
      const res = depFuncs[fKey](prop, newValue, readonly(modelValue.value));
      obj[fKey] = res;
    }
    fieldDep.value[p] = obj;
  }
}

// 远程搜索 TODO: 远程搜索？
async function handleRemote(keyword, item) {
  const prop = item.formItem.prop;
  const url =
    typeof item.url === "function"
      ? item.url(keyword, readonly(item))
      : item.url;
  const res = await service.get(url);
  remoteCache.value[prop] = res.data;
}

function fieldValueChange(prop, value, item) {
  // 修改事件
  if (attrs.onFieldChange && typeof attrs.onFieldChange === "function") {
    attrs.onFieldChange(prop, value, readonly(item));
  }
  // 字段修改事件
  if (
    attrs[`onFieldChange:${prop}`] &&
    typeof attrs[`onFieldChange:${prop}`] === "function"
  ) {
    attrs[`onFieldChange:${prop}`](value, readonly(item));
  }
  dispatchFieldUpdate(prop, value); // 触发依赖字段函数
}

// 重置
function handleReset() {
  formRef.value.resetFields();
  for (const key in resetFormData) {
    if (key in modelValue.value) {
      modelValue.value[key] = resetFormData[key];
      // 更新依赖
      dispatchFieldUpdate(key, resetFormData[key]);
    }
  }
  emits("reset");
}

/**
 * 提交数据
 */
async function handleSubmit() {
  if (props.triggerValidate) {
    formRef.value.validate((isValidate, rules) => {
      emits("submit", isValidate, rules);
    });
  } else {
    emits("submit", true, null);
  }
}

/**
 * 展开收起
 */
function handleExpand() {
  isExpand.value = !isExpand.value;
  emits("expandChange", isExpand.value);
}
defineExpose({
  get formInstance() {
    return formRef.value;
  },
});
</script>

<style scoped lang="scss">
.form-container {
  display: grid;
  grid-template-columns: repeat(var(--columns), 1fr);
  padding: var(--gap) 0;
  gap: var(--gap);
}
.form-item_label {
  display: flex;
  align-items: center;
  gap: 2px;
}
.form-container :deep(.el-upload) {
  --el-upload-dragger-padding-horizontal: 5px;
}
.form-container :deep(.el-upload .el-upload__text) {
  font-size: 12px;
}
.el-form-item {
  align-items: center;
  margin: 0;
}
.el-form-item :deep(.el-form-item__content) {
  flex-wrap: nowrap;
  gap: 5px;
}
.el-form-item :deep(.el-form-item__content > div:first-child) {
  flex: 1;
}
.el-form-item[data-show="false"] {
  display: none;
}
.expand .el-icon {
  transition: color 0.3s ease-in-out;
}
.expand .el-icon.collapse {
  transform: rotate(-180deg);
}
.el-form-item :deep(.el-upload-list:empty) {
  margin: 0;
}
// 内置样式 可用于formItems class样式
@for $n from 1 through 12 {
  .col-start-#{$n} {
    grid-column-start: $n;
  }
  .col-span-#{$n} {
    grid-column: span $n / span $n;
  }
}
</style>
